40. 始终使用 Override 注解

      主程序重复添加二十六个双字母组合到集合中,每个双字母组合由两个相同的小写字母组成。 然后它会打印集合的大小。 你可能希望程序打印 26,因为集合不能包含重复项。 如果你尝试运行程序,你会发现它打印的不是 26,而是 260。它有什么问题?

      显然,Bigram 类的作者打算重写 equals 方法(详见第 10 条),甚至记得重写 hashCode(详见第 11 条)。 不幸的是,我们倒霉的程序员没有重写 equals,而是重载它(详见第 52 条)。 要重写 Object.equals,必须定义一个 equals 方法,其参数的类型为 Object,但 Bigramequals 方法的参数不是 Object 类型的,因此 继承 Objectequals 方法,这个 equals 方法测试对象的引用是否是同一个,就像 == 运算符一样。 每个字母组合的 10 个副本中的每一个都与其他 9 个副本不同,所以它们被 Object.equals 视为不相等,这就解释了程序打印 260 的原因。

      如果插入此注解并尝试重新编译该程序,编译器将生成如下错误消息:

      你会立刻意识到你做错了什么,在额头上狠狠地打了一下,用一个正确的(详见第 10 条)来替换出错的 equals 实现:

      大多数 IDE 提供了是种使用 Override 注解的另一个理由。 如果启用适当的检查功能,如果有一个方法没有 Override 注解但是重写父类方法,则 IDE 将生成一个警告。 如果始终使用 Override 注解,这些警告将提醒你无意识的重写。 它们补充了编译器的错误消息,这些消息会提醒你无意识重写失败。 IDE 和编译器,可以确保你在任何你想要的地方和其他地方重写方法,万无一失。

      Override 注解可用于重写来自接口和类的方法声明。 随着 default 默认方法的出现,在接口方法的具体实现上使用 Override 以确保签名是正确的是一个好习惯。 如果知道某个接口没有默认方法,可以选择忽略接口方法的具体实现上的 Override 注解以减少混乱。

      总之,如果在每个方法声明中使用 注解,并且认为要重写父类声明,那么编译器可以保护免受很多错误的影响,但有一个例外。 在具体的类中,不需要注解标记你确信可以重写抽象方法声明的方法(尽管这样做也没有坏处)。